Skip to content

feat: Implement strict trace continuation#3567

Merged
antonis merged 28 commits intomainfrom
feat/strict-trace-continuation
Apr 30, 2026
Merged

feat: Implement strict trace continuation#3567
antonis merged 28 commits intomainfrom
feat/strict-trace-continuation

Conversation

@antonis
Copy link
Copy Markdown
Contributor

@antonis antonis commented Mar 17, 2026

📜 Description

Implement Strict Trace Continuation in the Dart SDK.

  • Add strictTraceContinuation (bool) and orgId (String?) options on SentryOptions
  • Extract org_id from DSN host; effectiveOrgId prefers explicit over DSN (trims whitespace)
  • Propagate sentry-org_id in baggage and trace context header
  • Validate incoming traces in SentryTransactionContext.fromSentryTrace per the spec decision matrix
  • Pass options through to the native Android SDK on init

💡 Motivation and Context

Prevents cross-organization trace continuation by validating org IDs in distributed traces.

Part of the Strict Trace Continuation initiative.

Platform coverage

Platform Coverage
Dart Full
Android Full — via sentry-android 8.39.1 (sentry-java#5136)
iOS Dart layer only — native support requires Sentry Cocoa v9 (#7705); Flutter is pinned to Sentry/HybridSDK 8.x

💚 How did you test it?

46 new unit tests covering DSN parsing, option resolution, decision matrix, baggage/header propagation, and fromSentryTrace validation.

📝 Checklist

  • I reviewed submitted code
  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPii is enabled
  • I updated the docs if needed
  • All tests passing
  • No breaking changes

🔮 Next steps

Changelog Entry

  • Prevent cross-organization trace continuation
    • By default, the SDK now extracts the organization ID from the DSN (e.g. o123.ingest.sentry.io) and compares it with the sentry-org_id value in incoming baggage headers. When the two differ, the SDK starts a fresh trace instead of continuing the foreign one. This guards against accidentally linking traces across organizations.
    • New option strictTraceContinuation (default false): when enabled, both the SDK's org ID and the incoming baggage org ID must be present and match for a trace to be continued. Traces with a missing org ID on either side are rejected.
    • New option orgId: allows explicitly setting the organization ID for self-hosted and Relay setups where it cannot be extracted from the DSN.
    • Options are also applied to the native Android SDK. On iOS, only the Dart layer enforces strict trace continuation.

- Parse org_id from DSN host (e.g. `o123.ingest.sentry.io` → `123`)
- Add `strictTraceContinuation` and `orgId` options to SentryOptions
- Propagate `sentry-org_id` in baggage/DSC
- Validate incoming traces: mismatched org IDs start a new trace;
  strict mode also rejects traces with missing org IDs

Spec: https://develop.sentry.dev/sdk/foundations/trace-propagation/#strict-trace-continuation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 17, 2026

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against e3d200a

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 17, 2026

iOS Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1247.42 ms 1241.66 ms -5.76 ms
Size 5.73 MiB 6.18 MiB 464.40 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
1fff351 1265.77 ms 1256.06 ms -9.70 ms
d2356d0 1257.04 ms 1257.94 ms 0.89 ms
c002f00 1252.47 ms 1258.78 ms 6.31 ms
cf443d2 1255.79 ms 1248.38 ms -7.40 ms
3f47ea3 1263.90 ms 1263.87 ms -0.02 ms
1ce780b 1252.49 ms 1256.17 ms 3.68 ms
0929dbf 1275.89 ms 1282.22 ms 6.33 ms
6b69699 1254.80 ms 1273.31 ms 18.52 ms
a10aff4 1241.67 ms 1255.02 ms 13.35 ms
fd88186 1255.06 ms 1252.76 ms -2.30 ms

App size

Revision Plain With Sentry Diff
1fff351 5.73 MiB 6.17 MiB 455.43 KiB
d2356d0 5.66 MiB 6.09 MiB 448.38 KiB
c002f00 5.65 MiB 6.09 MiB 448.38 KiB
cf443d2 5.53 MiB 6.00 MiB 479.99 KiB
3f47ea3 5.53 MiB 5.96 MiB 444.81 KiB
1ce780b 5.66 MiB 6.10 MiB 451.58 KiB
0929dbf 7.86 MiB 9.54 MiB 1.69 MiB
6b69699 7.86 MiB 9.44 MiB 1.58 MiB
a10aff4 5.53 MiB 6.00 MiB 486.71 KiB
fd88186 5.53 MiB 6.00 MiB 479.94 KiB

Previous results on branch: feat/strict-trace-continuation

Startup times

Revision Plain With Sentry Diff
a48e7d7 1239.53 ms 1238.38 ms -1.16 ms
e3bfa44 1238.25 ms 1234.53 ms -3.72 ms
223b1b3 1242.87 ms 1250.09 ms 7.21 ms
a1d3441 1224.39 ms 1229.85 ms 5.46 ms
4afe986 1264.88 ms 1255.48 ms -9.41 ms
d80a407 1254.05 ms 1256.48 ms 2.43 ms
25b589e 1265.35 ms 1262.23 ms -3.12 ms
7c249a7 1234.02 ms 1237.81 ms 3.79 ms
644dcd3 1239.36 ms 1246.24 ms 6.89 ms
f96a725 1263.67 ms 1268.90 ms 5.22 ms

App size

Revision Plain With Sentry Diff
a48e7d7 5.73 MiB 6.17 MiB 456.50 KiB
e3bfa44 5.73 MiB 6.17 MiB 456.50 KiB
223b1b3 5.73 MiB 6.18 MiB 464.42 KiB
a1d3441 5.73 MiB 6.18 MiB 464.46 KiB
4afe986 5.73 MiB 6.18 MiB 464.46 KiB
d80a407 5.73 MiB 6.18 MiB 464.41 KiB
25b589e 5.73 MiB 6.18 MiB 464.45 KiB
7c249a7 5.73 MiB 6.17 MiB 456.52 KiB
644dcd3 5.73 MiB 6.18 MiB 464.40 KiB
f96a725 5.73 MiB 6.17 MiB 456.50 KiB

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 17, 2026

Android Performance metrics 🚀

  Plain With Sentry Diff
Startup time 367.57 ms 363.79 ms -3.78 ms
Size 14.31 MiB 15.62 MiB 1.31 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
c97f488 502.43 ms 492.47 ms -9.97 ms
3801d52 397.53 ms 378.87 ms -18.66 ms
0929dbf 462.82 ms 492.76 ms 29.94 ms
426fbfd 368.10 ms 353.23 ms -14.87 ms
0e2b9b0 368.00 ms 356.72 ms -11.28 ms
cf443d2 464.64 ms 479.04 ms 14.40 ms
396cb30 405.70 ms 427.56 ms 21.86 ms
ce5c42b 401.52 ms 394.63 ms -6.90 ms
c8596a6 474.00 ms 492.96 ms 18.96 ms
2d34233 470.54 ms 558.90 ms 88.36 ms

App size

Revision Plain With Sentry Diff
c97f488 14.31 MiB 15.49 MiB 1.19 MiB
3801d52 14.31 MiB 15.49 MiB 1.19 MiB
0929dbf 6.54 MiB 7.70 MiB 1.17 MiB
426fbfd 13.93 MiB 15.06 MiB 1.13 MiB
0e2b9b0 14.30 MiB 15.49 MiB 1.19 MiB
cf443d2 13.93 MiB 15.00 MiB 1.06 MiB
396cb30 13.93 MiB 15.06 MiB 1.13 MiB
ce5c42b 14.31 MiB 15.49 MiB 1.19 MiB
c8596a6 6.54 MiB 7.53 MiB 1015.27 KiB
2d34233 6.54 MiB 7.55 MiB 1.01 MiB

Previous results on branch: feat/strict-trace-continuation

Startup times

Revision Plain With Sentry Diff
80a6525 386.66 ms 386.65 ms -0.01 ms
a48e7d7 371.18 ms 361.09 ms -10.09 ms
25b589e 437.27 ms 466.53 ms 29.26 ms
4afe986 385.21 ms 380.48 ms -4.73 ms
e3d200a 421.91 ms 432.42 ms 10.51 ms
f96a725 411.33 ms 420.06 ms 8.73 ms
9c81fe0 378.11 ms 382.77 ms 4.65 ms
e3bfa44 390.52 ms 373.63 ms -16.89 ms
7c249a7 366.72 ms 369.06 ms 2.34 ms
644dcd3 379.38 ms 366.17 ms -13.21 ms

App size

Revision Plain With Sentry Diff
80a6525 14.31 MiB 15.49 MiB 1.19 MiB
a48e7d7 14.31 MiB 15.49 MiB 1.19 MiB
25b589e 14.31 MiB 15.62 MiB 1.31 MiB
4afe986 14.31 MiB 15.62 MiB 1.31 MiB
e3d200a 14.31 MiB 15.49 MiB 1.19 MiB
f96a725 14.31 MiB 15.49 MiB 1.19 MiB
9c81fe0 14.31 MiB 15.62 MiB 1.31 MiB
e3bfa44 14.31 MiB 15.49 MiB 1.19 MiB
7c249a7 14.31 MiB 15.49 MiB 1.19 MiB
644dcd3 14.31 MiB 15.62 MiB 1.31 MiB

Copy link
Copy Markdown
Contributor Author

@antonis antonis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@buenaflor heads up that this is a fully AI generated implementation of the Strict Trace Continuation feature using the feature implementation skill

@antonis antonis marked this pull request as ready for review March 17, 2026 15:28
Comment thread packages/dart/lib/src/protocol/dsn.dart
Comment thread packages/dart/lib/src/utils/tracing_utils.dart Outdated
@antonis antonis marked this pull request as draft March 26, 2026 14:29
Copy link
Copy Markdown
Contributor Author

@antonis antonis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this back to draft to also handle the Android/iOS native dependencies.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 16, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


Features

  • Prevent cross-organization trace continuation by antonis in #3567
    • By default, the SDK now extracts the organization ID from the DSN (e.g. o123.ingest.sentry.io) and compares it with the sentry-org_id value in incoming baggage headers. When the two differ, the SDK starts a fresh trace instead of continuing the foreign one. This guards against accidentally linking traces across organizations.
    • New option strictTraceContinuation (default false): when enabled, both the SDK's org ID and the incoming baggage org ID must be present and match for a trace to be continued. Traces with a missing org ID on either side are rejected.
    • New option orgId: allows explicitly setting the organization ID for self-hosted and Relay setups where it cannot be extracted from the DSN.
    • Options are also applied to the native Android SDK. On iOS, only the Dart layer enforces strict trace continuation.

Fixes

  • (flutter) Send frame delay in seconds by buenaflor in #3677

Dependencies

Deps

  • chore(deps): update Native SDK to v0.13.8 by github-actions in #3667
  • chore(deps): update Android SDK to v8.40.0 by github-actions in #3663

Internal Changes

Deps

  • Bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.25.4 to 2.26.2 by dependabot in #3671
  • Bump getsentry/craft from 2.25.2 to 2.26.2 by dependabot in #3672
  • Bump actions/upload-artifact from 7.0.0 to 7.0.1 by dependabot in #3653

Other

  • Enforce conventional commit format for PR titles by buenaflor in #3666

🤖 This preview updates automatically when you update the PR.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.85%. Comparing base (153290c) to head (60de705).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3567      +/-   ##
==========================================
+ Coverage   86.82%   86.85%   +0.03%     
==========================================
  Files         335      335              
  Lines       11935    11969      +34     
==========================================
+ Hits        10362    10396      +34     
  Misses       1573     1573              
Flag Coverage Δ
sentry 86.76% <100.00%> (+0.07%) ⬆️
sentry_dio 97.73% <ø> (ø)
sentry_drift 93.57% <ø> (ø)
sentry_file 65.29% <ø> (ø)
sentry_firebase_remote_config 100.00% <ø> (ø)
sentry_flutter 91.17% <ø> (ø)
sentry_hive 77.48% <ø> (ø)
sentry_isar 74.37% <ø> (ø)
sentry_link 21.50% <ø> (ø)
sentry_logging 97.01% <ø> (ø)
sentry_sqflite 88.81% <ø> (ø)
sentry_supabase 97.27% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@antonis
Copy link
Copy Markdown
Contributor Author

antonis commented Apr 16, 2026

Status update: Merged latest main and moved changelog to Unreleased.

The Dart-layer implementation is self-contained and fully functional — trace propagation, org ID validation, and the decision matrix all work in pure Dart code.

To deliver the full functionality, the native SDK dependencies need to be bumped so that native-originated trace contexts also include org_id:

@sentry
Copy link
Copy Markdown

sentry Bot commented Apr 16, 2026

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
sentry_flutter_example io.sentry.flutter.sample 9.19.0 (1) Release

⚙️ sentry-flutter Build Distribution Settings

@buenaflor
Copy link
Copy Markdown
Contributor

@antonis I am not sure whether a backport is planned for v8 cocoa, so this feature may be blocked for now or we compromise and say that cocoa compatibility is limited for now

@antonis
Copy link
Copy Markdown
Contributor Author

antonis commented Apr 17, 2026

we compromise and say that cocoa compatibility is limited for now

Sounds good @buenaflor 👍 I'll proceed with that and open an issue to follow up when Cocoa is bumped

Comment thread packages/dart/lib/src/sentry_trace_context_header.dart
binarySizeTest:
diffMin: 900 KiB
diffMax: 1300 KiB
diffMax: 1350 KiB
Copy link
Copy Markdown
Contributor Author

@antonis antonis Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes led to ~1345 KiB, exceeding the 1300 KiB threshold

@antonis
Copy link
Copy Markdown
Contributor Author

antonis commented Apr 22, 2026

@sentry review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 223b1b3. Configure here.

@antonis
Copy link
Copy Markdown
Contributor Author

antonis commented Apr 22, 2026

we compromise and say that cocoa compatibility is limited for now

Sounds good @buenaflor 👍 I'll proceed with that and open an issue to follow up when Cocoa is bumped

This is now ready for another round 🙇

@antonis antonis marked this pull request as ready for review April 22, 2026 16:57
Copy link
Copy Markdown
Contributor

@buenaflor buenaflor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good overall, only a couple suggestions

Can you also create an issue for tracking support of native Cocoa SDK trace continuation option

Comment on lines +167 to +171
// Both missing is OK
if (sdkOrgId == null && baggageOrgId == null) {
return true;
}
// One missing means reject
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo we can remove these comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed with 168eb6f

sampled: json['sampled'],
replayId:
json['replay_id'] == null ? null : SentryId.fromId(json['replay_id']),
orgId: json['org_id'] as String?,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not used here yet but we can use this safe method:

Suggested change
orgId: json['org_id'] as String?,
orgId: json.getValueOrNull('org_id'),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting a warning after applying the change

The method 'getValueOrNull' isn't defined for the type 'AccessAwareMap'.
Check failure: The method 'getValueOrNull' isn't defined for the type 'AccessAwareMap'.
Try correcting the name to the name of an existing method, or defining a method named 'getValueOrNull'.
See https://dart.dev/diagnostics/undefined_method to learn more about this problem.
sentry-dart / analyze / analyze

Comment on lines +46 to +51
// Fall back to the current hub's options when not explicitly provided.
final effectiveOptions = options ?? Sentry.currentHub.options;

// Validate org ID before continuing the incoming trace.
if (!shouldContinueTrace(effectiveOptions, baggage?.getOrgId())) {
// Start a new trace instead of continuing the incoming one
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove the comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed with e9f7fc8

Comment thread CHANGELOG.md Outdated
Comment on lines +3 to +11
## Unreleased

### Features

- Prevent cross-organization trace continuation ([#3567](https://github.com/getsentry/sentry-dart/pull/3567))
- By default, the SDK now extracts the organization ID from the DSN (e.g. `o123.ingest.sentry.io`) and compares it with the `sentry-org_id` value in incoming baggage headers. When the two differ, the SDK starts a fresh trace instead of continuing the foreign one. This guards against accidentally linking traces across organizations.
- New option `strictTraceContinuation` (default `false`): when enabled, both the SDK's org ID **and** the incoming baggage org ID must be present and match for a trace to be continued. Traces with a missing org ID on either side are rejected.
- New option `orgId`: allows explicitly setting the organization ID for self-hosted and Relay setups where it cannot be extracted from the DSN.
- Options are also applied to the native Android SDK. On iOS, only the Dart layer enforces strict trace continuation.
Copy link
Copy Markdown
Contributor

@buenaflor buenaflor Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this, we use auto changelogs now, you can instead write this changelog in the PR description under

### Changelog Entry

e.g

### Changelog Entry

- Prevent cross-organization trace continuation
  - By default, the SDK now extracts the organization ID from the DSN (e.g. `o123.ingest.sentry.io`) and compares it with the `sentry-org_id` value in incoming baggage headers. When the two differ, the SDK starts a fresh trace instead of continuing the foreign one. This guards against accidentally linking traces across organizations.
  - New option `strictTraceContinuation` (default `false`): when enabled, both the SDK's org ID **and** the incoming baggage org ID must be present and match for a trace to be continued. Traces with a missing org ID on either side are rejected.
  - New option `orgId`: allows explicitly setting the organization ID for self-hosted and Relay setups where it cannot be extracted from the DSN.
  - Options are also applied to the native Android SDK. On iOS, only the Dart layer enforces strict trace continuation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍 Removed with 65b1c56
We should adopt this in RN too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the changelog entry, we dont need the PR link in there

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see how it looks like in the changelog preview

sampled: json['sampled'],
replayId:
json['replay_id'] == null ? null : SentryId.fromId(json['replay_id']),
orgId: json.getValueOrNull('org_id'),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the import is missing because getValueOrNull is an extension method

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added with 60de705

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@antonis antonis requested a review from buenaflor April 30, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants